package com.corecloud.config;

import com.alibaba.fastjson.JSONObject;
import com.corecloud.entity.SysLogEntity;
import com.corecloud.mapper.mysql.SysLogMapper;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;


/**
 * 日志过滤器
 */
@Order(1)
@Component
@WebFilter(filterName = "webLogFilter", urlPatterns = "/")
public class WebLogFilter implements Filter {

	@Autowired
	private SysLogMapper sysLogMapper;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {

	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) response);

		chain.doFilter(request, responseWrapper);

		String resultStr = new String(responseWrapper.getContent(), "UTF-8");
		// 在得到响应的数据之后，response的输出流中就无可用的数据，所以需要巴数据放回去
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.write(responseWrapper.getContent());
		outputStream.flush();
		outputStream.close();

		String url = ((HttpServletRequest) request).getRequestURI();
		if(url.indexOf("/add") >= 0 || url.indexOf("/delete") >= 0 || url.indexOf("/save") >= 0 || url.indexOf("/update") >= 0){
			SysLogEntity sysLog = new SysLogEntity();
			sysLog.setUrl(url);
			Map paras = request.getParameterMap();
			Gson gson = new Gson();
			if(paras!=null && paras.size() > 0){
				sysLog.setParas(gson.toJson(paras));
			}
			Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
			if (!(authentication instanceof AnonymousAuthenticationToken) && authentication != null && !StringUtils.isEmpty(authentication.getName())) {
				sysLog.setUserName(authentication.getName());
			}
			sysLog.setCreateTime(new Date());

			if(!StringUtils.isEmpty(resultStr)){
				sysLog.setMsg(resultStr);
			}
			sysLogMapper.insert(sysLog);
		}

	}

	@Override
	public void destroy() {

	}


	class RequestWrapper extends HttpServletRequestWrapper {
		private String body;

		/**
		 * Constructs a request object wrapping the given request.
		 *
		 * @param request The request to wrap
		 * @throws IllegalArgumentException if the request is null
		 */
		public RequestWrapper(HttpServletRequest request) throws IOException {
			super(request);
			// 此处是得到请求内容的长度
			Integer contentLength = request.getContentLength();
			if(contentLength != null && contentLength > 0){
				StringBuilder stringBuilder = new StringBuilder(contentLength);
				BufferedReader bufferedReader = null;
				InputStream inputStream = null;
				try {
					// 输入流获取请求的数据
					inputStream = request.getInputStream();
					if (inputStream != null) {
						bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
						char[] charBuffer = new char[1024];
						int bytesRead;
						while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
							stringBuilder.append(charBuffer, 0, bytesRead);
						}
					}
				} catch (IOException e) {
					throw e;
				} finally {
					if (inputStream != null) {
						inputStream.close();
					}
					if (bufferedReader != null) {
						bufferedReader.close();
					}
				}
				// 下面会把数据放回request的输入流中，所以要把数据恢复到与之前请求内容相同的长度
				body = String.format("%-" + contentLength + "s", JSONObject.parseObject(stringBuilder.toString()));
			}
		}

		@Override
		public ServletInputStream getInputStream() throws IOException {
			// 数据放到字节流中
			ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.getBody().getBytes());
			return new ServletInputStream() {
				@Override
				public boolean isFinished() {
					return false;
				}

				@Override
				public boolean isReady() {
					return false;
				}

				@Override
				public void setReadListener(ReadListener listener) {
				}

				@Override
				public int read() throws IOException {
					// 数据放回request的输入流中
					return byteArrayInputStream.read();
				}
			};
		}

		@Override
		public BufferedReader getReader() throws IOException {
			return new BufferedReader(new InputStreamReader(this.getInputStream()));
		}

		public String getBody() {
			// 请求中的数据
			return this.body;
		}
	}

	class ResponseWrapper extends HttpServletResponseWrapper {
		private ByteArrayOutputStream byteArrayOutputStream;
		private ServletOutputStream servletOutputStream;

		/**
		 * Constructs a response adaptor wrapping the given response.
		 *
		 * @param response The response to be wrapped
		 * @throws IllegalArgumentException if the response is null
		 */
		public ResponseWrapper(HttpServletResponse response) throws IOException {
			super(response);
			byteArrayOutputStream = new ByteArrayOutputStream();
			servletOutputStream = new MyServletOutputStream(byteArrayOutputStream);
		}

		@Override
		public ServletOutputStream getOutputStream() throws IOException {
			return servletOutputStream;
		}

		@Override
		public PrintWriter getWriter() throws IOException {
			return new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, StandardCharsets.UTF_8));
		}

		@Override
		public void flushBuffer() {
			if (servletOutputStream != null) {
				try {
					servletOutputStream.flush();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		public byte[] getContent() {
			flushBuffer();
			// response中的数据
			return byteArrayOutputStream.toByteArray();
		}

		class MyServletOutputStream extends ServletOutputStream {
			// 把response输出流中的数据写入字节流中
			private ByteArrayOutputStream byteArrayOutputStream;

			public MyServletOutputStream(ByteArrayOutputStream byteArrayOutputStream) {
				this.byteArrayOutputStream = byteArrayOutputStream;
			}

			@Override
			public boolean isReady() {
				return false;
			}

			@Override
			public void setWriteListener(WriteListener listener) {
			}

			@Override
			public void write(int b) throws IOException {
				byteArrayOutputStream.write(b);
			}
		}
	}
}
